#include <iostream>
#include "Poco/DateTime.h"
#include "Poco/DateTimeParser.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/LocalDateTime.h"
#include "Poco/Timezone.h"
#include "Poco/Timespan.h"
#include "Poco/Exception.h"
#include "String/String.h"
#include "DateTime.h"

using namespace SystemTool;

// Construct the date time
DateTime::DateTime()
{
    m_pDateTime = nullptr;
}

// Destruct the date time
DateTime::~DateTime()
{
    if (m_pDateTime)
    {
        delete (Poco::LocalDateTime *)m_pDateTime;
        m_pDateTime = nullptr;
    }
}

// Copy construct
DateTime::DateTime(const DateTime &other)
{
    m_pDateTime = new Poco::LocalDateTime;
    if (other.m_pDateTime)
    {
        *((Poco::LocalDateTime *)m_pDateTime) = *((Poco::LocalDateTime *)other.m_pDateTime);
    }
    SetDateTime();
}

// Assigment
DateTime &DateTime::operator=(const DateTime &other)
{
    if (this != &other)
    {
        if (m_pDateTime)
        {
            delete (Poco::LocalDateTime *)m_pDateTime;
            m_pDateTime = nullptr;
        }

        m_pDateTime = new Poco::LocalDateTime;
        if (other.m_pDateTime)
        {
            *((Poco::LocalDateTime *)m_pDateTime) = *((Poco::LocalDateTime *)other.m_pDateTime);
        }
        SetDateTime();
    }
    return *this;
}

// Get current time
DateTime DateTime::Now()
{
    DateTime dt;
    try
    {
        Poco::LocalDateTime *pCurDateTime = new Poco::LocalDateTime;
        dt.m_pDateTime = pCurDateTime;
        dt.SetDateTime();
    }
    catch (const Poco::Exception &e)
    {
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }

    return dt;
}

// Get time from string "2024-03-08 22:10:00" / "2024-3-8 22:10:00"
DateTime DateTime::FromString(std::string strStandardTime)
{
    if (strStandardTime.empty())
    {
        return DateTime();
    }

    DateTime MyDt;
    try
    {
        int tzd = 0;
        Poco::DateTime dt;
        Poco::DateTimeParser::parse(strStandardTime, dt, tzd);

        Poco::LocalDateTime *pLocalTime = new Poco::LocalDateTime(tzd, dt);
        MyDt.m_pDateTime = pLocalTime;
        MyDt.SetDateTime();
    }
    catch (const Poco::Exception &e)
    {
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }

    return MyDt;
}

// Timestamp to time
DateTime DateTime::FromSTime_t(time_t iTime_S)
{
    DateTime dt;
    try
    {
        Poco::Timestamp st = Poco::Timestamp::fromEpochTime(iTime_S);
        Poco::DateTime dDateTime(st);

        Poco::LocalDateTime *pLocalTime = new Poco::LocalDateTime(dDateTime);
        dt.m_pDateTime = pLocalTime;
        dt.SetDateTime();
    }
    catch (const Poco::Exception &e)
    {
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }

    return dt;
}

// Timestamp to time
DateTime DateTime::FromMsTime_t(time_t iTime_Ms)
{
    DateTime dt;
    try
    {
        Poco::Timestamp st = Poco::Timestamp::fromEpochTime(iTime_Ms / 1000);
        Poco::DateTime dDateTime(st);

        Poco::LocalDateTime *pLocalTime = new Poco::LocalDateTime(dDateTime);
        dt.m_pDateTime = pLocalTime;
        dt.SetDateTime();
    }
    catch (const Poco::Exception &e)
    {
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }

    return dt;
}

// Equal
bool DateTime::operator==(DateTime &other)
{
    return GetYear() == other.GetYear() 
    && GetMonth() == other.GetMonth() 
    && GetDay() == other.GetDay() 
    && GetHour() == other.GetHour() 
    && GetMinute() == other.GetMinute() 
    && GetSecond() == other.GetSecond() 
    && GetMilliSecond() == other.GetMilliSecond() 
    && GetMicroSecond() == other.GetMicroSecond();
}

// Get time's timestamp
time_t DateTime::ToSTime_t()
{
    if (m_pDateTime == nullptr)
    {
        return 0;
    }
    Poco::LocalDateTime &ldt = *((Poco::LocalDateTime *)m_pDateTime);
    Poco::DateTime dt = ldt.utc();
    return dt.timestamp().epochTime();
}

// Get time's timestamp
time_t DateTime::ToMsTime_t()
{
    if (m_pDateTime == nullptr)
    {
        return 0;
    }
    Poco::LocalDateTime &ldt = *((Poco::LocalDateTime *)m_pDateTime);
    Poco::DateTime dt = ldt.utc();
    return dt.timestamp().epochTime() * 1000;
}

// Time to string
std::string DateTime::ToString()
{
    std::string strTime = "1970-01-01 00:00:00";

    if (m_pDateTime == nullptr)
    {
        return strTime;
    }

    Poco::LocalDateTime *pTime = ((Poco::LocalDateTime *)m_pDateTime);
    try
    {
        Poco::Timestamp ts = pTime->timestamp();
        strTime = Poco::DateTimeFormatter::format(ts, Poco::DateTimeFormat::SORTABLE_FORMAT);
    }
    catch (const Poco::Exception &e)
    {
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }

    return strTime;
}

// Set the dt
void DateTime::SetDateTime()
{
    SetYear_Inner();
    SetMonth_Inner();
    SetDay_Inner();
    SetHour_Inner();
    SetMinute_Inner();
    SetSecond_Inner();
    SetMilliSecond_Inner();
    SetMicroSecond_Inner();
}
        

// Set time's year
void DateTime::SetYear_Inner()
{
    if (m_pDateTime == nullptr)
    {
        return;
    }
    m_iYear = ((Poco::LocalDateTime *)m_pDateTime)->year();
}

// Set time's month
void DateTime::SetMonth_Inner()
{
    if (m_pDateTime == nullptr)
    {
        return;
    }
    m_iMonth = ((Poco::LocalDateTime *)m_pDateTime)->month();
}

// Set time's day
void DateTime::SetDay_Inner()
{
    if (m_pDateTime == nullptr)
    {
        return;
    }
    m_iDay = ((Poco::LocalDateTime *)m_pDateTime)->day();
}

// Set time's hour
void DateTime::SetHour_Inner()
{
    if (m_pDateTime == nullptr)
    {
        return;
    }
    m_iHour = ((Poco::LocalDateTime *)m_pDateTime)->hour();
}

// Set time's minute
void DateTime::SetMinute_Inner()
{
    if (m_pDateTime == nullptr)
    {
        return;
    }
    m_iMinute = ((Poco::LocalDateTime *)m_pDateTime)->minute();
}

// Set time's second
void DateTime::SetSecond_Inner()
{
    if (m_pDateTime == nullptr)
    {
        return;
    }
    m_iSecond = ((Poco::LocalDateTime *)m_pDateTime)->second();
}

// Set time's millisecond
void DateTime::SetMilliSecond_Inner()
{
    if (m_pDateTime == nullptr)
    {
        return;
    }
    m_iMillisecond = ((Poco::LocalDateTime *)m_pDateTime)->millisecond();
}

// Set time's microsecond
void DateTime::SetMicroSecond_Inner()
{
    if (m_pDateTime == nullptr)
    {
        return;
    }
    m_iMicrosecond = ((Poco::LocalDateTime *)m_pDateTime)->microsecond();
}

// Get time's year
int DateTime::GetYear()
{
    return m_iYear;
}

// Get time's month
int DateTime::GetMonth()
{
    return m_iMonth;
}

// Get time's day
int DateTime::GetDay()
{
   return m_iDay;
}

// Get time's hour
int DateTime::GetHour()
{
   return m_iHour;
}

// Get time's minute
int DateTime::GetMinute()
{
   return m_iMinute;
}

// Get time's second
int DateTime::GetSecond()
{
   return m_iSecond;
}

// Get time's millisecond
int DateTime::GetMilliSecond()
{
   return m_iMillisecond;
}

// Get time's microsecond
int DateTime::GetMicroSecond()
{
    return m_iMicrosecond;
}
